﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CommonUtils;

namespace SearchBackEnd
{
    class CounterVector
    {
        /// <summary>
        /// map from end point address to number of pages
        /// </summary>
        private Dictionary<String, VectorValues> counters = new Dictionary<string, VectorValues>();

        /// <summary>
        /// increases the count of a specific entry in counter vector by one.
        /// </summary>
        /// <param name="endPointAddress"></param>
        /// <param name="isPrimary"></param>
        public void IncCount(String endPointAddress, Boolean isPrimary)
        {
            if (!counters.ContainsKey(endPointAddress)) return;
            if (isPrimary)
                counters[endPointAddress].PrimaryPageCount++;
            else
                counters[endPointAddress].SecondaryPageCount++;
            PrintVector();
        }

        /// <summary>
        /// returns the primary & secondary page count for a specific server.
        /// </summary>
        /// <param name="endPointAddress"></param>
        /// <returns>current vector values</returns>
        public VectorValues GetCounts(String endPointAddress)
        {
            return counters[endPointAddress];
        }

        /// <summary>
        /// returns the primary or secondary page count for a specific server.
        /// </summary>
        /// <param name="endPointAddress"></param>
        /// <param name="isPrimary"></param>
        /// <returns>the primary or secondary page count for a specific server.</returns>
        public long GetCounts(String endPointAddress, Boolean isPrimary)
        {
            return isPrimary ? counters[endPointAddress].PrimaryPageCount : counters[endPointAddress].SecondaryPageCount;
        }

        /// <summary>
        /// updates the count of a specific server in the vector
        /// </summary>
        /// <param name="endPointAddress"></param>
        /// <param name="val"></param>
        public void UpdateCounts(String endPointAddress, VectorValues val)
        {
            counters[endPointAddress] = val;
            PrintVector();
        }

        /// <summary>
        /// removes all counts of a specific server.
        /// </summary>
        /// <param name="endPointAddress"></param>
        public void RemoveCounts(String endPointAddress)
        {
            counters.Remove(endPointAddress);
        }

        /// <summary>
        /// Gets the server with the minumum primary/secondary page count.
        /// </summary>
        /// <param name="isPrimary"></param>
        /// <returns></returns>
        public String GetMinCountEp(Boolean isPrimary)
        {
            return GetMinCountEp(null, isPrimary);
        }

        /// <summary>
        /// Gets the server with the minimum primary/secondary page count such that its address isn't notThisAddr.
        /// </summary>
        /// <param name="isPrimary"></param>
        /// <returns></returns>
        public String GetMinCountEp(String notThisAddr, Boolean isPrimary)
        {
            return GetMinCountEp(notThisAddr, isPrimary, long.MaxValue, long.MaxValue);
        }

        /// <summary>
        /// Gets the server with the minimum primary/secondary page count such that:
        ///   1. its address isn't notThisAddr.
        ///   2. it is smaller or equal then maxCount
        ///   3. if it is equal to maxCount then the other count is smaller then maxCount2
        /// </summary>
        /// <param name="notThisAddr"></param>
        /// <param name="isPrimary"></param>
        /// <param name="maxCount"></param>
        /// <param name="maxCount2"></param>
        /// <returns></returns>
        public String GetMinCountEp(string notThisAddr, bool isPrimary, long maxCount, long maxCount2)
        {
            long min = maxCount;
            long min2 = maxCount2;
            String minEpAddr = null;
            foreach (String epAddr in counters.Keys)
            {
                VectorValues vv = counters[epAddr];
                if ((!isPrimary || vv.PrimaryPageCount <= min) &&
                    (isPrimary || vv.SecondaryPageCount <= min) &&
                    (notThisAddr == null || notThisAddr != epAddr))
                {
                    if (isPrimary)
                    {
                        if (vv.PrimaryPageCount == min && vv.SecondaryPageCount >= min2) continue;
                        min = vv.PrimaryPageCount;
                        min2 = vv.SecondaryPageCount;
                    }
                    else
                    {
                        if (vv.SecondaryPageCount == min && vv.PrimaryPageCount >= min2) continue;
                        min = vv.SecondaryPageCount;
                        min2 = vv.PrimaryPageCount;
                    }
                    minEpAddr = epAddr;
                }
            }
            return minEpAddr;
        }

        /// <summary>
        /// Creates an 0,0 entry in the vector if such an entry doesn't already exist.
        /// </summary>
        /// <param name="serverId">the key to add.</param>
        public void CreateEntry(string serverId)
        {
            if (!counters.ContainsKey(serverId)) UpdateCounts(serverId, new VectorValues());
        }

        /// <summary>
        /// Gets the average count of primary/secondary pages per server - rounded down.
        /// </summary>
        /// <param name="isPrimary"></param>
        /// <returns></returns>
        public long GetAvarageCount(Boolean isPrimary)
        {
            long total = 0;
            foreach (String epAddr in counters.Keys)
            {
                total += (isPrimary) ? counters[epAddr].PrimaryPageCount : counters[epAddr].SecondaryPageCount;
            }
            return total / counters.Count;
        }

        public void PrintVector()
        {
            Print.DebugEmit("vector is now:");
            foreach (String epAddr in counters.Keys)
            {
                Print.DebugEmit(epAddr + " - P: " + counters[epAddr].PrimaryPageCount + " - S: " + counters[epAddr].SecondaryPageCount);
            }
        }


        [Serializable]
        public class VectorValues
        {
            public VectorValues(long PrimaryPageCount, long SecondaryPageCount)
            {
                this.PrimaryPageCount = PrimaryPageCount;
                this.SecondaryPageCount = SecondaryPageCount;
            }

            public VectorValues()
            {
                PrimaryPageCount = 0;
                SecondaryPageCount = 0;
            }
            public long PrimaryPageCount { get; set; }
            public long SecondaryPageCount { get; set; }
        };

    }
}
